﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Xml;
using GraphSharp.Controls;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using WPFExtensions.ViewModel.Commanding;
using XmlStudio.Controllers.Interfaces;
using XmlStudio.DataLayer;
using XmlStudio.Dialogs;
using XmlStudio.Enums;
using XmlStudio.GraphSharp;
using XmlStudio.Helper;
using XmlStudio.Interfaces;
using XmlStudio.Models.Events;
using XmlStudio.Settings;
using XmlStudio.ViewModel.Node;
using System.IO;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Windows.Media;
using System.Text;
using System.Diagnostics;
using Tomers.WPF.Localization;

namespace XmlStudio.ViewModel {
    /// <summary>
    /// ViewModel for the XML document editing.
    /// </summary>
    public class XmlViewModel : CommandSink, INotifyPropertyChanged {
        #region Fields
        #region Public
        #region Commands
        /// <summary>
        /// Relayouts the rendered graph and positions vertices and edges to their default positions according
        /// to the selected layout algorithm.
        /// </summary>
        public static readonly RoutedCommand RelayoutCommand = new RoutedCommand("Relayout", typeof(XmlViewModel));

        /// <summary>
        /// Exits the application.
        /// </summary>
        public static readonly RoutedCommand ExitCommand = new RoutedCommand("Exit", typeof(XmlViewModel));

        /// <summary>
        /// Calls the controller to expand the selected param.
        /// </summary>
        public static readonly RoutedCommand ExpandNodeCommand = new RoutedCommand("ExpandNode", typeof(XmlViewModel));

        /// <summary>
        /// Calls the controller to expand the selected node and all its descendants.
        /// </summary>
        public static readonly RoutedCommand ExpandNodeWithSubtreeCommand = new RoutedCommand("ExpandNodeWithSubtree", typeof(XmlViewModel));

        /// <summary>
        /// Calls the controller to collapse the selected param.
        /// </summary>
        public static readonly RoutedCommand CollapseNodeCommand = new RoutedCommand("CollapseNode", typeof(XmlViewModel));

        /// <summary>
        /// Pops up the dialog to edit the selected element.
        /// </summary>
        public static readonly RoutedCommand EditXmlElementCommand = new RoutedCommand("EditXmlElement", typeof(XmlViewModel));

        /// <summary>
        /// Edits either element of text node.
        /// </summary>
        public static readonly RoutedCommand EditXmlNodeCommand = new RoutedCommand("EditXmlNode", typeof(XmlViewModel));

        /// <summary>
        /// Saves the selected modified element.
        /// </summary>
        public static readonly RoutedCommand SaveXmlElementCommand = new RoutedCommand("SaveXmlElement", typeof(XmlViewModel));

        /// <summary>
        /// Calls the controller to save the document to the underlying file.
        /// </summary>
        public static readonly RoutedCommand SaveDocumentCommand = new RoutedCommand("SaveDocument", typeof(XmlViewModel));

        /// <summary>
        /// Pops up the dialog to specify the path where to save the file and then calls the controller to save the document.
        /// </summary>
        public static readonly RoutedCommand SaveDocumentAsCommand = new RoutedCommand("SaveDocumentAs", typeof(XmlViewModel));

        /// <summary>
        /// Pops up the dialog to edit the selected text param.
        /// </summary>
        public static readonly RoutedCommand EditTextNodeCommand = new RoutedCommand("EditTextNode", typeof(XmlViewModel));

        /// <summary>
        /// Pops up the dialog to change xmlReaderSettings.
        /// </summary>
        public static readonly RoutedCommand SettingsCommand = new RoutedCommand("Settings", typeof(XmlViewModel));

        /// <summary>
        /// Pops up the dialog to add new child element.
        /// </summary>
        public static readonly RoutedCommand AddChildElementCommand = new RoutedCommand("AddChildElement", typeof(XmlViewModel));

        /// <summary>
        /// Adds text node as the last child to the currently selected element.
        /// </summary>
        public static readonly RoutedCommand AddChildTextNodeCommand = new RoutedCommand("AddChildTextNode", typeof(XmlViewModel));

        /// <summary>
        /// Creates new root element.
        /// </summary>
        public static readonly RoutedCommand CreateRootElementCommand = new RoutedCommand("CreateRootElement", typeof(XmlViewModel));

        /// <summary>
        /// Removes currently selected node.
        /// </summary>
        public static readonly RoutedCommand RemoveNodeCommand = new RoutedCommand("RemoveNode", typeof(XmlViewModel));

        /// <summary>
        /// Expands all nodes in the document.
        /// </summary>
        public static readonly RoutedCommand ExpandAllCommand = new RoutedCommand("ExpandAll", typeof(XmlViewModel));

        /// <summary>
        /// Validates document against XSD (if present).
        /// </summary>
        public static readonly RoutedCommand ValidateXsdCommand = new RoutedCommand("ValidateXsd", typeof(XmlViewModel));

        /// <summary>
        /// Cancels the XSD validation.
        /// </summary>
        public static readonly RoutedCommand CancelValidateXsdCommand = new RoutedCommand("CancelValidateXsd", typeof(XmlViewModel));

        /// <summary>
        /// Validates document against DTD (if present).
        /// </summary>
        public static readonly RoutedCommand ValidateDtdCommand = new RoutedCommand("ValidateDtd", typeof(XmlViewModel));

        /// <summary>
        /// Cancels the DTD validation.
        /// </summary>
        public static readonly RoutedCommand CancelValidateDtdCommand = new RoutedCommand("CancelValidateDtd", typeof(XmlViewModel));

        /// <summary>
        /// Attaches DTD to the current document (if there is one, it is replaced).
        /// </summary>
        public static readonly RoutedCommand AttachDtdCommand = new RoutedCommand("AttachDtd", typeof(XmlViewModel));

        /// <summary>
        /// Removes DTD from the current document.
        /// </summary>
        public static readonly RoutedCommand RemoveDtdCommand = new RoutedCommand("RemoveDtd", typeof(XmlViewModel));

        /// <summary>
        /// Selects nodes according to the specified XPath query.
        /// </summary>
        public static readonly RoutedCommand SelectNodesCommand = new RoutedCommand("SelectNodes", typeof(XmlViewModel));

        /// <summary>
        /// Exports document to the specified image format.
        /// </summary>
        public static readonly RoutedCommand ExportCommand = new RoutedCommand("Export", typeof(XmlViewModel));

        /// <summary>
        /// Displays dialog to change the colors of currently selected nodes.
        /// </summary>
        public static readonly RoutedCommand ChangeColorCommand = new RoutedCommand("ChangeColor", typeof(XmlViewModel));

        /// <summary>
        /// Display dialog to change the shape of currently selected nodes.
        /// </summary>
        public static readonly RoutedCommand ChangeAppearanceCommand = new RoutedCommand("ChangeAppearance", typeof(XmlViewModel));

        /// <summary>
        /// Moves node right (among its sibling nodes).
        /// </summary>
        public static readonly RoutedCommand MoveNodeRightCommand = new RoutedCommand("MoveNodeRight", typeof(XmlViewModel));

        /// <summary>
        /// Moves node left (among its sibling nodes).
        /// </summary>
        public static readonly RoutedCommand MoveNodeLeftCommand = new RoutedCommand("MoveNodeLeft", typeof(XmlViewModel));

        /// <summary>
        /// Selects currently selected node as a source from where to copy the subtree.
        /// </summary>
        public static readonly RoutedCommand SelectSubtreeForCopyingCommand = new RoutedCommand("SelectSubtreeForCopying", typeof(XmlViewModel));

        /// <summary>
        /// Selects currently selected node as a source from where to move the subtree.
        /// </summary>
        public static readonly RoutedCommand SelectSubtreeForMovingCommand = new RoutedCommand("SelectSubtreeForMoving", typeof(XmlViewModel));

        /// <summary>
        /// Pastes the selected subtree as a child of currently selected node.
        /// </summary>
        public static readonly RoutedCommand PasteSubtreeCommand = new RoutedCommand("PasteSubtree", typeof(XmlViewModel));

        /// <summary>
        /// Saves the visualization (colors, shape...) to a separate file.
        /// </summary>
        public static readonly RoutedCommand SaveVisualizationCommand = new RoutedCommand("SaveVisualization", typeof(XmlViewModel));
        #endregion
        #endregion

        #region Protected
        /// <summary>
        /// Graph.
        /// </summary>
        protected XmlGraph graph;

        /// <summary>
        /// Layout algorithm type.
        /// </summary>
        protected string layoutAlgorithmType;
        /// <summary>
        /// The controller.
        /// </summary>
        protected readonly IController controller;
        /// <summary>
        /// The model.
        /// </summary>
        protected readonly IXmlModel model;
        /// <summary>
        /// The view.
        /// </summary>
        protected readonly IXmlView view;
        /// <summary>
        /// Collection of nodes.
        /// </summary>
        protected readonly ObservableDictionary<BigXmlNode, XmlNodeViewModel> nodes = new ObservableDictionary<BigXmlNode, XmlNodeViewModel>();
        /// <summary>
        /// Collection of appearances.
        /// </summary>
        protected readonly Dictionary<BigXmlNode, VisualizationInfo> appearances = new Dictionary<BigXmlNode, VisualizationInfo>();
        /// <summary>
        /// Root node.
        /// </summary>
        protected XmlNodeViewModel root;
        /// <summary>
        /// String with raw XML.
        /// </summary>
        protected string rawXml;
        /// <summary>
        /// Application.
        /// </summary>
        protected readonly App application;
        /// <summary>
        /// Indicates whether validating XSD.
        /// </summary>
        protected bool validatingXsd;
        /// <summary>
        /// Indicates whether validating DTD.
        /// </summary>
        protected bool validatingDtd;
        /// <summary>
        /// Indicates whether subtree has been selected.
        /// </summary>
        protected bool subtreeSelected;
        /// <summary>
        /// The subtree for moving or copying, its root.
        /// </summary>
        protected XmlNodeViewModel subtreeForCopyingOrMoving;
        private SubtreeOperation subtreeOperation;
        /// <summary>
        /// Exception policy.
        /// </summary>
        protected const string DefaultExceptionPolicy = "UIPolicy";
        /// <summary>
        /// Root context menu.
        /// </summary>
        protected System.Windows.Controls.ContextMenu rootContextMenu;
        #endregion

        #region Private
        private SelectionService selectionService = null;
        private const int nodeLimit = 500;
        #endregion
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="XmlViewModel"/> class.
        /// </summary>
        public XmlViewModel(App application) {
            this.application = application;
            this.rootContextMenu = this.FindResource("DesignerCanvasContextMenu") as System.Windows.Controls.ContextMenu;

            RegisterCommands();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="XmlViewModel"/> class.
        /// </summary>
        /// <param name="application"></param>
        /// <param name="controller"><see cref="IController"/> to set.</param>
        /// <param name="xmlModel"><see cref="IXmlModel"/> to set.</param>
        /// <param name="view"></param>
        public XmlViewModel(App application, IController controller, IXmlModel model, IXmlView view)
            : this(application) {
            this.controller = controller;
            this.view = view;
            this.layoutAlgorithmType = XmlStudioSettings.Instance.DefaultLayoutAlgorithmType.ToString();
            this.model = model;

            SubscribeModelEvents();
            SubscribeViewEvents();


            if(!string.IsNullOrEmpty(this.model.Path)) {
                this.OpenFile(this.model.Path);
                //this.controller.GetRootElement();
            }
        }
        #endregion

        #region Properties
        #region Public
        /// <summary>
        /// Gets the context menu for canvas.
        /// </summary>
        public virtual System.Windows.Controls.ContextMenu RootContextMenu {
            get {
                return this.rootContextMenu;
            }
        }

        /// <summary>
        /// Gets the command target for context menu (workaround for a bug in wpf?).
        /// </summary>
        public object CommandTarget { get { return this.view as MainWindow; } }

        /// <summary>
        /// Gets the current application mode.
        /// </summary>
        public string Mode {
            get { return this.model.Mode.ToString(); }
        }

        /// <summary>
        /// Gets or sets the <see cref="XmlGraph"/> to display.
        /// </summary>
        public XmlGraph Graph {
            get { return this.graph; }

            set {
                this.graph = value;
                OnPropertyChanged("Graph");
            }
        }

        /// <summary>
        /// Gets the number of vertices in currently rendered graph.
        /// </summary>
        public int VertexCount {
            get {
                if(this.Graph == null) {
                    return 0;
                }

                return this.Graph.VertexCount;
            }
        }

        /// <summary>
        /// Gets the number of edges in currently rendered graph.
        /// </summary>
        public int EdgeCount {
            get {
                if(this.Graph == null) {
                    return 0;
                }

                return this.Graph.EdgeCount;
            }
        }

        /// <summary>
        /// Gets the number of nodes added to the collection of nodes.
        /// </summary>
        public int KeyCount {
            get {
                return this.nodes.Count;
            }
        }

        /// <summary>
        /// Gets or sets the raw xml of the current document associated with the view xmlModel.
        /// </summary>
        public string RawXml {
            get { return this.rawXml; }
            set {
                this.rawXml = value;
                OnPropertyChanged("RawXml");
            }
        }

        /// <summary>
        /// Gets or sets the type of layout algorithm to use when displaying the graph.
        /// </summary>
        public string LayoutAlgorithmType {
            get { return layoutAlgorithmType; }
            set {
                if(value != layoutAlgorithmType) {
                    this.layoutAlgorithmType = value;
                    OnPropertyChanged("LayoutAlgorithmType");
                }
            }
        }

        /// <summary>
        /// Gets the string to show in title bar.
        /// </summary>
        public string TitleBar {
            get {
                return string.Format("{0}{1} - {2}", this.model.DocumentName, model.Modified ? "*" : string.Empty, Properties.Settings.Default.applicationName);
            }
        }

        /// <summary>
        /// Gets the value indicating whether there is a XSD validation in progress.
        /// </summary>
        public bool ValidatingXsd {
            get { return this.validatingXsd; }
            private set { this.validatingXsd = value; OnPropertyChanged("ValidatingXsd"); }
        }

        /// <summary>
        /// Gets the value indicating whether there is a DTD validation in progress.
        /// </summary>
        public bool ValidatingDtd {
            get { return this.validatingDtd; }
            private set { this.validatingDtd = value; OnPropertyChanged("ValidatingDtd"); }
        }

        /// <summary>
        /// Gets the <see cref="Visibility"/> value indicating whether XML tools are visible in ribbon.
        /// </summary>
        public Visibility XmlToolsVisible {
            get {
                Visibility visibility = Visibility.Collapsed;
                //if(this.model.Mode == ApplicationMode.Xml) {
                if(this.SelectionService.CurrentSelection.Count > 0) {
                    visibility = Visibility.Visible;
                } else {
                    visibility = Visibility.Collapsed;
                }
                //}
                Debug.WriteLine(string.Format("XmlToolVisible = {0} (HashCode = {1})", visibility, this.GetHashCode()));
                return visibility;
            }
        }

        /// <summary>
        /// Gets the value indicating whether the XML tools are selected or not (it depends on their visibility).
        /// </summary>
        public bool XmlToolsSelected {
            get {
                if(XmlToolsVisible == Visibility.Visible) {
                    Debug.WriteLine(string.Format("XmlToolSelected = {0} (HashCode = {1})", "true", this.GetHashCode()));
                    return true;
                } else {
                    Debug.WriteLine(string.Format("XmlToolSelected = {0} (HashCode = {1})", "false", this.GetHashCode()));
                    return false;
                }
            }
        }
        #endregion

        #region Protected
        /// <summary>
        /// Gets or sets the value indicating whether a subtree has been selected.
        /// </summary>
        protected bool SubtreeSelected {
            get { return this.subtreeSelected; }
            set {
                this.subtreeSelected = value;
                OnPropertyChanged("SubtreeSelected");
            }
        }
        #endregion

        #region Private
        /// <summary>
        /// Gets the <see cref="SelectionService"/> associated with the current view.
        /// </summary>
        private SelectionService SelectionService {
            get {
                if(selectionService == null) {
                    selectionService = SelectionService.GetCurrentInstance((this.view as MainWindow).designerCanvas);
                }

                return selectionService;
            }
        }
        #endregion
        #endregion

        #region Methods
        #region Event handlers
        /// <summary>
        /// Invoked when the property of the <see cref="SelectionService"/> changes.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void SelectionServicePropertyChanged(object sender, PropertyChangedEventArgs e) {
            // when the current selection changes, raise event that XmlToolsVisible changed so bindings in GUI get updated
            if(e.PropertyName == "CurrentSelection") {
                this.OnPropertyChanged("XmlToolsVisible");
                this.OnPropertyChanged("XmlToolsSelected");
            }
        }

        /// <summary>
        /// Invoked when there is a change in nodes dictionary.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void NodesDictionaryChanged(object sender, System.Collections.Specialized.NotifyDictionaryChangedEventArgs<BigXmlNode, XmlNodeViewModel> e) {
            this.OnPropertyChanged("KeyCount");
        }

        /// <summary>
        /// Invoked when the particular subtree in the model has been updated and needs to be re-rendered.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void ModelSubtreeUpdated(object sender, SubtreeUpdatedEventArgs<BigXmlNode> e) {
            try {
                XmlNodeViewModel node;
                if(this.nodes.TryGetValue(e.Root, out node)) {
                    RenderNodeRecursively(node);
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Called when root element changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="XmlStudio.Models.Events.RootElementChangedEventArgs"/> instance containing the event data.</param>
        protected virtual void OnModelRootElementChanged(object sender, RootElementChangedEventArgs e) {
            try {
                var oldRoot = this.root;
                this.RemoveFromGraph(oldRoot);

                XmlNodeViewModel node;
                if(this.nodes.TryGetValue(e.NewRoot, out node)) {
                    this.root = node;
                    RenderNodeRecursively(node);
                } else {
                    this.root = CreateVertex(e.NewRoot, this.model.Mode, this.CommandTarget as IInputElement);
                    this.AddToGraph(this.root, null);
                    //RenderNodeRecursively(this.root);
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Called when progress changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.ProgressChangedEventArgs"/> instance containing the event data.</param>
        protected virtual void OnModelProgressChanged(object sender, ProgressChangedEventArgs e) {
            this.view.SetProgressBarValue(e.ProgressPercentage);
        }

        /// <summary>
        /// Called when save completed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnModelSaveCompleted(object sender, EventArgs e) {
            this.view.SetProgressBarVisibility(false);
        }

        /// <summary>
        /// Invoked when some nodes in model has been selected using XPath.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void ModelNodesSelected(object sender, NodesSelectedEventArgs<BigXmlNode> e) {
            try {
                var selectedNodes = e.Nodes;

                var list = new List<XmlNodeViewModel>();

                foreach(var node in selectedNodes) {
                    XmlNodeViewModel nodeViewModel;
                    if(this.nodes.TryGetValue(node, out nodeViewModel)) {
                        list.Add(nodeViewModel);
                    }
                }

                SelectNodes(list);
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when DTD validation has completed.
        /// </summary>
        /// <param name="sender">The object where the event handler has been attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void ModelValidateDtdCompleted(object sender, RunWorkerCompletedEventArgs e) {
            try {
                this.ValidatingDtd = false;

                if(e.Error != null) {
                    this.AddMessageToStatusBar(e.Error.ToString());
                } else if(e.Cancelled) {
                    this.AddMessageToStatusBar(LanguageDictionary.GetDictionary(LanguageContext.Instance.Culture).Translate<string>("viewModel.OperationCancelled", "Text"));
                } else {
                    this.AddMessageToStatusBar(LanguageDictionary.GetDictionary(LanguageContext.Instance.Culture).Translate<string>("viewModel.ValidationFinished", "Text"));
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when XSD validation has completed.
        /// </summary>
        /// <param name="sender">The object where the event handler has been attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void ModelValidateXsdCompleted(object sender, RunWorkerCompletedEventArgs e) {
            try {
                this.ValidatingXsd = false;

                if(e.Error != null) {
                    this.AddMessageToStatusBar(e.Error.ToString());
                } else if(e.Cancelled) {
                    this.AddMessageToStatusBar(LanguageDictionary.GetDictionary(LanguageContext.Instance.Culture).Translate<string>("viewModel.OperationCancelled", "Text"));
                } else {
                    this.AddMessageToStatusBar(LanguageDictionary.GetDictionary(LanguageContext.Instance.Culture).Translate<string>("viewModel.ValidationFinished", "Text"));
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when status message should be added.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void ModelStatusUpdated(object sender, StatusUpdatedEventArgs e) {
            try {
                this.AddMessageToStatusBar(e.Message);
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when there is a validation error or message.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void ModelValidating(object sender, System.Xml.Schema.ValidationEventArgs e) {
            try {
                this.AddMessageToStatusBar(string.Format(LanguageDictionary.GetDictionary(LanguageContext.Instance.Culture).Translate<string>("viewModel.Validation", "Text") + ": {0} {1}", e.Severity, e.Message));
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when window is being closed.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void WindowClosing(object sender, CancelEventArgs e) {
            try {
                OnCloseDialog(e);
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when node has been collapsed.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void ModelNodeCollapsed(object sender, NodeCollapsedEventArgs<BigXmlNode> e) {
            try {
                XmlNodeViewModel node;

                if(this.nodes.TryGetValue(e.Node, out node)) {
                    if(e.Node.Parent != null) {
                        XmlNodeViewModel parent;
                        if(this.nodes.TryGetValue(e.Node.Parent, out parent)) {
                        }
                    }

                    this.RemoveChildVerticesFromGraph(node);
                    if(node is XmlElementViewModel) {
                        ((XmlElementViewModel)node).State = e.Node.NodeState;
                    }
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked after node has been expanded.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void OnNodeExpanded(object sender, NodeExpandedEventArgs<BigXmlNode> e) {
            try {
                XmlNodeViewModel node;
                // try to find the expanded param in our collection of nodes
                if(this.nodes.TryGetValue(e.Node, out node)) {
                    if(e.Node.Parent != null) {
                        // the same for the possible param param
                        XmlNodeViewModel parent;
                        this.nodes.TryGetValue(e.Node.Parent, out parent);
                    }

                    // render the expanded param recursively
                    RenderNodeRecursively(node);

                    // update the state of the expanded param
                    if(node is XmlElementViewModel) {
                        var nodeVm = node as XmlElementViewModel;
                        nodeVm.State = e.Node.NodeState;
                    }
                } else {
                    throw new InvalidOperationException(LanguageDictionary.GetDictionary(LanguageContext.Instance.Culture).Translate<string>("viewModel.CouldNotExpandNode", "Text"));
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when an attribute has been added to a certain element.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void ModelAttributeAdded(object sender, AttributeAddedEventArgs<BigXmlNode, IBigXmlAttribute> e) {
            try {
                if(this.nodes[e.Node] != null) {
                    var element = this.nodes[e.Node];
                    element.Refresh();
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when an attribute of a certain element has been removed.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void ModelAttributeRemoved(object sender, AttributeRemovedEventArgs<BigXmlNode, IBigXmlAttribute> e) {
            try {
                if(this.nodes[e.Node] != null) {
                    var element = this.nodes[e.Node];
                    element.Refresh();
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when some node has been removed.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void ModelNodeRemoved(object sender, NodeRemovedEventArgs<BigXmlNode> e) {
            try {
                var element = this.nodes[e.RemovedNode];
                if(element == this.root) {
                    this.root = null;
                }

                this.RemoveFromGraph(element);
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when the root element has been added.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void ModelRootElementAdded(object sender, RootElementAddedEventArgs<BigXmlNode> e) {
            try {
                var newRoot = CreateVertex(e.Root, this.model.Mode, this.view as MainWindow); //new XmlElementViewModel(e.Root);

                if(this.Graph != null) {
                    // unload event handlers
                    this.UnsubscribeGraphEvents();
                }

                this.Graph = new XmlGraph();
                this.SubscribeGraphEvents();
                this.Graph.AddVertex(newRoot);

                if(this.root != null) {
                    this.RemoveNode(this.root);
                }

                this.AddNode(e.Root, newRoot);
                this.root = newRoot;
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when an edge has been removed from the graph.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected virtual void OnGraphEdgeRemoved(XmlNodeEdge e) {
            try {
                OnPropertyChanged("EdgeCount");
                OnPropertyChanged("VertexCount");
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when an edge has been added to the graph.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected virtual void OnGraphEdgeAdded(XmlNodeEdge e) {
            try {
                OnPropertyChanged("EdgeCount");
                OnPropertyChanged("VertexCount");
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when a vertex has been removed from the graph.
        /// </summary>
        /// <param name="vertex">Removed vertex.</param>
        protected virtual void OnGraphVertexRemoved(XmlNodeViewModel vertex) {
            try {
                OnPropertyChanged("VertexCount");
                OnPropertyChanged("EdgeCount");
                // Relayout(); // so that XmlNodeViewModel instances don't stay referenced by layout manager...
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when a vertex has been added to the graph.
        /// </summary>
        /// <param name="vertex">Added vertex.</param>
        protected virtual void OnGraphVertexAdded(XmlNodeViewModel vertex) {
            try {
                OnPropertyChanged("VertexCount");
                OnPropertyChanged("EdgeCount");
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when a node has been added to the document.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void OnModelNodeAdded(object sender, NodeAddedEventArgs<BigXmlNode> e) {
            // var param = this.nodes[e.Parent] as XmlElementViewModel;
            try {
                XmlNodeViewModel nodeToAdd = CreateVertex(e.AddedNode, this.model.Mode, this.view as MainWindow);
                XmlNodeViewModel parentNode = null;
                XmlNodeViewModel referenceNode = null;
                //this.nodes.TryGetValue(e.AddedNode, out nodeToAdd);

                if(e.Parent != null) {
                    this.nodes.TryGetValue(e.Parent, out parentNode);
                }

                if(e.ReferenceNode != null) {
                    this.nodes.TryGetValue(e.ReferenceNode, out referenceNode);
                }

                if(nodeToAdd != null) {
                    //if(e.InsertMode == InsertMode.Last) {
                    //    var newElement = new XmlElementViewModel(e.AddedNode);
                    //    RenderNode(newElement, param);
                    //} else {
                    this.InsertAfter(nodeToAdd, parentNode, e.InsertMode, referenceNode);
                    //}
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when some property of the model has changed.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected void OnModelPropertyChanged(object sender, PropertyChangedEventArgs e) {
            try {
                switch(e.PropertyName) {
                    case "Modified":
                        OnPropertyChanged("TitleBar");
                        break;
                    case "Path":
                        OnPropertyChanged("TitleBar");
                        break;
                    case "Mode":
                        this.OnPropertyChanged("Mode");
                        break;
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when a certain node has been updated from the model.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void OnNodeUpdated(object sender, NodeUpdatedEventArgs<BigXmlNode> e) {
            try {
                if(this.nodes[e.UpdatedNode] != null) {
                    var element = this.nodes[e.UpdatedNode];
                    element.Refresh();
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked after raw xml in the model has changed.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void OnRawXmlChanged(object sender, RawXmlChangedEventArgs e) {
            try {
                this.RawXml = this.controller.GetRawXml();
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked after a property of <see cref="XmlViewModel"/> changed.
        /// </summary>
        /// <param name="propertyName">Property name.</param>
        protected virtual void OnPropertyChanged(string propertyName) {
            if(PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// Invoked when application is about to close.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected virtual void OnCloseDialog(CancelEventArgs e) {
            try {
                if(this.model.Modified) {
                    var dialog = new ApplicationExitDialog(e);
                    dialog.Owner = this.view as Window;
                    var res = dialog.ShowDialog();
                    if(res.HasValue && res.Value) {
                        this.controller.Save();
                    }
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Invoked when a property of <see cref="XmlNodeViewModel"/> changes.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        protected virtual void OnNodeViewModelPropertyChanged(object sender, PropertyChangedEventArgs e) {
            try {
                if(e.PropertyName == "VertexControl") {
                    if(sender is XmlNodeViewModel) {
                        var nodeViewModel = sender as XmlNodeViewModel;
                        SetAppearance(nodeViewModel);
                    }
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region Helpers
        /// <summary>
        /// Cleanups this instance.
        /// </summary>
        public void Cleanup() {
            this.rootContextMenu = null;
            this.UnsubscribeFromEvents();
            this.UnregisterCommands();
            this.selectionService = null;
            this.nodes.Clear();
            this.appearances.Clear();
        }

        /// <summary>
        /// Sets either saved appearance or leaves the default one.
        /// </summary>
        /// <param name="node"></param>
        private void SetAppearance(XmlNodeViewModel node) {

            if(node.Node.Appearance == null) {
                // first try to find saved visualization info
                var info = (from x in this.LoadedVisualizationInfo where x.Address.SequenceEqual(node.Node.Address) select x).FirstOrDefault();
                //info = this.LoadedVisualizationInfo.
                if(info != null) {
                    node.VertexControl.FillBrush = info.Details.Background;
                    node.VertexControl.StrokeBrush = info.Details.Stroke;
                    node.VertexControl.AppearanceTemplate = this.FindResource(info.Details.AppearanceTemplate) as ControlTemplate;
                    node.VertexControl.AppearanceTemplateName = info.Details.AppearanceTemplate;
                    node.Node.Appearance = info.Details;
                }

                // and then user might've changed it, so try to find it too (this one has priority because it was changed later)
                VisualizationInfo appearance;
                if(this.appearances.TryGetValue(node.Node, out appearance)) {
                    node.VertexControl.FillBrush = appearance.Details.Background;
                    node.VertexControl.StrokeBrush = appearance.Details.Stroke;
                    node.VertexControl.AppearanceTemplate = this.FindResource(appearance.Details.AppearanceTemplate) as ControlTemplate;
                    node.VertexControl.AppearanceTemplateName = appearance.Details.AppearanceTemplate;
                    node.Node.Appearance = info.Details;
                }
            } else {
                var appearance = node.Node.Appearance as VisualizationInfoDetails;
                if(appearance != null) {
                    node.VertexControl.FillBrush = appearance.Background;
                    node.VertexControl.StrokeBrush = appearance.Stroke;
                    node.VertexControl.AppearanceTemplate = this.FindResource(appearance.AppearanceTemplate) as ControlTemplate;
                    node.VertexControl.AppearanceTemplateName = appearance.AppearanceTemplate;
                }
            }
        }

        /// <summary>
        /// Selects the nodes in the collection passed as a parameter.
        /// </summary>
        /// <param name="nodes">Nodes to select.</param>
        private void SelectNodes(IEnumerable<XmlNodeViewModel> nodes) {
            try {
                this.SelectionService.ClearSelection();
                foreach(var node in nodes) {
                    this.SelectionService.AddToSelection(node.VertexControl);
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Renders all descendant nodes of the specified node.
        /// </summary>
        /// <param name="node">Parent node which descendants to render.</param>
        protected void RenderNodeRecursively(XmlNodeViewModel node) {
            try {
                // first step: remove all its descendants from canvas
                this.RemoveChildVerticesFromGraph(node);

                // then add all new descendant nodes to canvas
                foreach(var child in node.Node.ChildNodes.Where(x => x != null)) {
                    var vertex = CreateVertex(child, this.model.Mode, this.view as MainWindow);
                    this.AddToGraph(vertex, node);
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Adds message to the status bar.
        /// </summary>
        /// <param name="message">Message to add.</param>
        protected void AddMessageToStatusBar(string message) {
            try {
                this.view.AddMessageToStatus(message);
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Registers the commands.
        /// </summary>
        private void RegisterCommands() {
            try {
                #region RelayoutCommand
                RegisterCommand(RelayoutCommand, param => CanRelayout(), param => Relayout());
                RelayoutCommand.InputGestures.Add(new KeyGesture(Key.R, ModifierKeys.Control));
                #endregion

                #region ExitCommand
                RegisterCommand(ExitCommand, param => CanExit(), param => Exit());
                ExitCommand.InputGestures.Add(new KeyGesture(Key.X, ModifierKeys.Control));
                #endregion

                #region CloseCommand
                RegisterCommand(ApplicationCommands.Close, param => CanClose(), param => Close());
                #endregion

                #region OpenCommand
                RegisterCommand(ApplicationCommands.Open, param => CanOpen(), param => OpenFile());
                ApplicationCommands.Open.InputGestures.Add(new KeyGesture(Key.O, ModifierKeys.Control));
                #endregion

                #region NewCommand
                RegisterCommand(ApplicationCommands.New, param => CanNewDocument(), param => NewDocument(param as string));
                ApplicationCommands.New.InputGestures.Add(new KeyGesture(Key.N, ModifierKeys.Control));
                #endregion

                #region ExpandNodeCommand
                RegisterCommand(ExpandNodeCommand, param => CanExpandNode(param as XmlElementViewModel), param => ExpandNode(param as XmlElementViewModel));

                RegisterCommand(ExpandNodeWithSubtreeCommand, param => CanExpandWithSubtree(param as XmlElementViewModel), param => ExpandNodeWithSubtree(param as XmlElementViewModel));
                #endregion

                #region CollapseNodeCommand
                RegisterCommand(CollapseNodeCommand, param => CanCollapseNode(param as XmlElementViewModel), param => CollapseNode(param as XmlElementViewModel));
                #endregion

                #region EditXmlElementCommand
                RegisterCommand(EditXmlElementCommand, param => CanEditXmlElement(), param => EditXmlElement(param as XmlElementViewModel));
                #endregion

                #region EditXmlNodeCommand
                RegisterCommand(EditXmlNodeCommand, param => CanEditXmlNode(), param => EditXmlNode());
                #endregion

                #region SaveXmlElementCommand
                RegisterCommand(SaveXmlElementCommand, param => CanSaveXmlElement(param as XmlElementViewModel), param => SaveXmlElement(param as XmlElementViewModel));
                #endregion

                #region SaveVisualization
                RegisterCommand(SaveVisualizationCommand, param => CanSaveVisualization(), param => SaveVisualization());
                #endregion

                #region UndoCommand
                RegisterCommand(ApplicationCommands.Undo, param => this.model.CanUndo, param => Undo());
                ApplicationCommands.Undo.InputGestures.Add(new KeyGesture(Key.Z, ModifierKeys.Control));
                #endregion

                #region RedoCommand
                RegisterCommand(ApplicationCommands.Redo, param => this.model.CanRedo, param => Redo());
                ApplicationCommands.Redo.InputGestures.Add(new KeyGesture(Key.Y, ModifierKeys.Control));
                #endregion

                #region SaveDocumentCommand
                RegisterCommand(SaveDocumentCommand, param => CanSaveDocument(), param => SaveDocument());
                SaveDocumentCommand.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
                #endregion

                #region SaveDocumentAsCommand
                RegisterCommand(SaveDocumentAsCommand, param => CanSaveDocumentAs(), param => SaveDocumentAs());
                #endregion

                #region EditTextNodeCommand
                RegisterCommand(EditTextNodeCommand, param => CanEditTextNode(), param => EditTextNode(param as XmlTextViewModel));
                #endregion

                #region SetttingsCommand
                RegisterCommand(SettingsCommand, param => true, param => Settings());
                #endregion

                #region AddChildElementCommand
                RegisterCommand(AddChildElementCommand, param => CanAddChildElement(param as XmlElementViewModel), param => AddChildElement(param as XmlElementViewModel));

                RegisterCommand(AddChildTextNodeCommand, param => CanAddChildTextNode(param as XmlElementViewModel), param => AddChildTextNode(param as XmlElementViewModel));
                #endregion

                #region CreateRootElementCommand
                RegisterCommand(CreateRootElementCommand, param => CanCreateRootElement(), param => CreateRootElement());
                #endregion

                #region RemoveNodeCommand
                RegisterCommand(RemoveNodeCommand, param => CanRemoveNode(), param => RemoveNode(param as XmlNodeViewModel));
                #endregion

                #region ExpandAllCommand
                RegisterCommand(ExpandAllCommand, param => CanExpandAll(), param => ExpandAll());
                #endregion

                #region ValidateXsdCommand
                RegisterCommand(ValidateXsdCommand, param => CanValidateXsd(), param => ValidateXsd());

                RegisterCommand(CancelValidateXsdCommand, param => this.ValidatingXsd, param => CancelValidateXsd());
                #endregion

                #region ValidateDtdCommand
                RegisterCommand(ValidateDtdCommand, param => CanValidateDtd(), param => ValidateDtd());

                RegisterCommand(CancelValidateDtdCommand, param => this.ValidatingDtd, param => CancelValidateDtd());

                RegisterCommand(AttachDtdCommand, param => CanAttachDtd(), param => AttachDtd());

                RegisterCommand(RemoveDtdCommand, param => CanRemoveDtd(), param => RemoveDtd());
                #endregion

                #region SelectNodesCommand
                RegisterCommand(SelectNodesCommand, param => CanSelectNodes(), param => SelectNodes(param as string));
                #endregion

                #region ExportCommand
                RegisterCommand(ExportCommand, param => CanExport(), param => Export());
                #endregion

                #region PrintCommand
                RegisterCommand(ApplicationCommands.Print, param => CanPrint(), param => Print());
                #endregion

                #region ChangeColorCommand
                RegisterCommand(ChangeColorCommand, param => CanChangeColor(), param => ChangeColor());
                #endregion

                #region ChangeAppearanceCommand
                RegisterCommand(ChangeAppearanceCommand, param => CanChangeAppearance(), param => ChangeAppearance());
                #endregion

                #region MoveNode Commands
                RegisterCommand(MoveNodeLeftCommand, param => CanMoveNode(param as XmlNodeViewModel), param => MoveNodeLeft(param as XmlNodeViewModel));

                RegisterCommand(MoveNodeRightCommand, param => CanMoveNode(param as XmlNodeViewModel), param => MoveNodeRight(param as XmlNodeViewModel));
                #endregion

                #region Copy and move commands
                RegisterCommand(SelectSubtreeForCopyingCommand, param => CanSelectSubtreeForCopying(param as XmlNodeViewModel), param => SelectSubtreeForCopying(param as XmlNodeViewModel));

                RegisterCommand(SelectSubtreeForMovingCommand, param => CanSelectSubtreeForMoving(param as XmlNodeViewModel), param => SelectSubtreeForMoving(param as XmlNodeViewModel));

                RegisterCommand(PasteSubtreeCommand, param => CanPasteSubtree(), param => PasteSubtree(param as XmlNodeViewModel));
                #endregion
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Unregisters commands.
        /// </summary>
        public virtual void UnregisterCommands() {
            this.UnregisterCommand(RelayoutCommand);
            this.UnregisterCommand(ExitCommand);
            this.UnregisterCommand(ApplicationCommands.Close);
            this.UnregisterCommand(ApplicationCommands.Open);
            this.UnregisterCommand(ApplicationCommands.New);
            this.UnregisterCommand(ExpandNodeCommand);
            this.UnregisterCommand(ExpandNodeWithSubtreeCommand);
            this.UnregisterCommand(CollapseNodeCommand);
            this.UnregisterCommand(EditXmlNodeCommand);
            this.UnregisterCommand(EditXmlElementCommand);
            this.UnregisterCommand(SaveXmlElementCommand);
            this.UnregisterCommand(SaveVisualizationCommand);
            this.UnregisterCommand(ApplicationCommands.Undo);
            this.UnregisterCommand(ApplicationCommands.Redo);
            this.UnregisterCommand(SaveDocumentCommand);
            this.UnregisterCommand(SaveDocumentAsCommand);
            this.UnregisterCommand(EditTextNodeCommand);
            this.UnregisterCommand(SettingsCommand);
            this.UnregisterCommand(AddChildElementCommand);
            this.UnregisterCommand(AddChildTextNodeCommand);
            this.UnregisterCommand(CreateRootElementCommand);
            this.UnregisterCommand(RemoveNodeCommand);
            this.UnregisterCommand(ExpandAllCommand);
            this.UnregisterCommand(ValidateXsdCommand);
            this.UnregisterCommand(CancelValidateXsdCommand);
            this.UnregisterCommand(ValidateDtdCommand);
            this.UnregisterCommand(CancelValidateDtdCommand);
            this.UnregisterCommand(AttachDtdCommand);
            this.UnregisterCommand(RemoveDtdCommand);
            this.UnregisterCommand(SelectNodesCommand);
            this.UnregisterCommand(ExportCommand);
            this.UnregisterCommand(ApplicationCommands.Print);
            this.UnregisterCommand(ChangeColorCommand);
            this.UnregisterCommand(ChangeAppearanceCommand);
            this.UnregisterCommand(MoveNodeLeftCommand);
            this.UnregisterCommand(MoveNodeRightCommand);
            this.UnregisterCommand(SelectSubtreeForCopyingCommand);
            this.UnregisterCommand(SelectSubtreeForMovingCommand);
            this.UnregisterCommand(PasteSubtreeCommand);
        }

        /// <summary>
        /// Subscribes for the model's events.
        /// </summary>
        protected void SubscribeModelEvents() {
            try {
                this.model.NodeExpanded += OnNodeExpanded;
                this.model.RawXmlChanged += OnRawXmlChanged;
                this.model.NodeUpdated += OnNodeUpdated;
                this.model.AttributeAdded += ModelAttributeAdded;
                this.model.AttributeRemoved += ModelAttributeRemoved;
                this.model.PropertyChanged += OnModelPropertyChanged;
                this.model.NodeAdded += OnModelNodeAdded;
                this.model.RootElementAdded += ModelRootElementAdded;
                this.model.NodeRemoved += ModelNodeRemoved;
                this.model.NodeCollapsed += ModelNodeCollapsed;
                this.model.Validating += ModelValidating;
                this.model.StatusUpdated += ModelStatusUpdated;
                this.model.ValidateXsdCompleted += ModelValidateXsdCompleted;
                this.model.ValidateDtdCompleted += ModelValidateDtdCompleted;
                this.model.NodesSelected += ModelNodesSelected;
                this.model.SubtreeUpdated += ModelSubtreeUpdated;
                this.model.RootElementChanged += OnModelRootElementChanged;
                this.model.ProgressChanged += OnModelProgressChanged;
                this.model.SaveCompleted += OnModelSaveCompleted;

                var window = this.view as Window;
                if(window != null) {
                    window.Closing += WindowClosing;
                }

                this.nodes.DictionaryChanged += NodesDictionaryChanged;
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Unsubscribes from the model's events.
        /// </summary>
        protected void UnsubscribeModelEvents() {
            try {
                this.model.NodeExpanded -= OnNodeExpanded;
                this.model.RawXmlChanged -= OnRawXmlChanged;
                this.model.NodeUpdated -= OnNodeUpdated;
                this.model.AttributeAdded -= ModelAttributeAdded;
                this.model.AttributeRemoved -= ModelAttributeRemoved;
                this.model.PropertyChanged -= OnModelPropertyChanged;
                this.model.NodeAdded -= OnModelNodeAdded;
                this.model.RootElementAdded -= ModelRootElementAdded;
                this.model.NodeRemoved -= ModelNodeRemoved;
                this.model.NodeCollapsed -= ModelNodeCollapsed;
                this.model.Validating -= ModelValidating;
                this.model.StatusUpdated -= ModelStatusUpdated;
                this.model.ValidateXsdCompleted -= ModelValidateXsdCompleted;
                this.model.ValidateDtdCompleted -= ModelValidateDtdCompleted;
                this.model.NodesSelected -= ModelNodesSelected;
                this.model.SubtreeUpdated -= ModelSubtreeUpdated;
                this.model.RootElementChanged -= OnModelRootElementChanged;
                this.model.ProgressChanged -= OnModelProgressChanged;

                var window = this.view as Window;
                if(window != null) {
                    window.Closing -= WindowClosing;
                }

                this.nodes.DictionaryChanged -= NodesDictionaryChanged;
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Subscribes for the view's events.
        /// </summary>
        protected void SubscribeViewEvents() {
            if(this.view != null) {
                //var selectionService = SelectionService.GetCurrentInstance((this.view as MainWindow).designerCanvas);
                this.SelectionService.PropertyChanged += SelectionServicePropertyChanged;
            }
        }

        /// <summary>
        /// Unsubscribes from the view's events.
        /// </summary>
        protected void UnsubscribeViewEvents() {
            if(this.view != null) {
                //var selectionService = SelectionService.GetCurrentInstance((this.view as MainWindow).designerCanvas);
                this.SelectionService.PropertyChanged -= SelectionServicePropertyChanged;
            }
        }

        /// <summary>
        /// Subscribes for the graph's events.
        /// </summary>
        protected void SubscribeGraphEvents() {
            try {
                this.Graph.VertexAdded += OnGraphVertexAdded;
                this.Graph.VertexRemoved += OnGraphVertexRemoved;
                this.Graph.EdgeAdded += OnGraphEdgeAdded;
                this.Graph.EdgeRemoved += OnGraphEdgeRemoved;
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Unsubscribes from the graph's events.
        /// </summary>
        protected void UnsubscribeGraphEvents() {
            try {
                if(this.Graph != null) {
                    this.Graph.VertexAdded -= OnGraphVertexAdded;
                    this.Graph.VertexRemoved -= OnGraphVertexRemoved;
                    this.Graph.EdgeAdded -= OnGraphEdgeAdded;
                    this.Graph.EdgeRemoved -= OnGraphEdgeRemoved;
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Unsubscribes from events.
        /// </summary>
        public virtual void UnsubscribeFromEvents() {
            this.UnsubscribeGraphEvents();
            this.UnsubscribeModelEvents();
            this.UnsubscribeViewEvents();
        }

        /// <summary>
        /// Adds <paramref name="nodeViewModel"/> to the collection of <see cref="XmlNodeViewModel"/>.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="nodeViewModel">Reference to the param.</param>
        private void AddNode(BigXmlNode node, XmlNodeViewModel nodeViewModel) {
            try {
                nodeViewModel.PropertyChanged += OnNodeViewModelPropertyChanged;
                this.nodes.Add(node, nodeViewModel);
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Removes <see cref="BigXmlNode"/> the from the nodes dictionary.
        /// </summary>
        /// <param name="node">The node.</param>
        private void RemoveNode(BigXmlNode node) {
            try {
                if(this.nodes.ContainsKey(node)) {
                    var viewModel = this.nodes[node];
                    viewModel.PropertyChanged -= OnNodeViewModelPropertyChanged;
                    this.nodes.Remove(node);

                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Removes vertex and all its child vertices (nodes) from the rendered graph and param dictionary.
        /// </summary>
        /// <param name="vertex">The vertex.</param>
        private void RemoveFromGraph(XmlNodeViewModel vertex) {
            try {
                if(vertex != null) {
                    IEnumerable<XmlNodeEdge> outEdges;
                    if(this.Graph.TryGetOutEdges(vertex, out outEdges)) {
                        var outvertices = from x in outEdges select x.Target;
                        var edgesToRemove = outEdges.ToList();

                        foreach(var v in outvertices.ToList()) {
                            RemoveFromGraph(v);
                        }

                        foreach(var edge in edgesToRemove) {
                            this.Graph.RemoveEdge(edge);
                        }
                    }

                    this.Graph.RemoveVertex(vertex);
                    vertex.PropertyChanged -= OnNodeViewModelPropertyChanged;
                    this.RemoveNode(vertex.Node);
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Removes child vertices of a specified vertex from the graph.
        /// </summary>
        /// <param name="vertex">Parent vertex which children to remove.</param>
        private void RemoveChildVerticesFromGraph(XmlNodeViewModel vertex) {
            try {
                IEnumerable<XmlNodeEdge> outEdges;
                if(this.Graph.TryGetOutEdges(vertex, out outEdges)) {
                    var outvertices = from x in outEdges select x.Target;

                    foreach(var v in outvertices.ToList()) {
                        RemoveFromGraph(v);
                    }

                    foreach(var edge in outEdges.ToList()) {
                        this.Graph.RemoveEdge(edge);
                    }
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Creates <see cref="XmlNodeViewModel"/> from <see cref="BigXmlNode"/>.
        /// </summary>
        /// <param name="node">Node from model.</param>
        /// <param name="mode">Application mode.</param>
        /// <param name="commandTarget">Command target for context menu (this is a workaround because context menu is always disabled without this).</param>
        /// <returns>Created <see cref="XmlNodeViewModel"/>.</returns>
        protected static XmlNodeViewModel CreateVertex(BigXmlNode node, ApplicationMode mode, IInputElement commandTarget) {
            try {
                if(node != null) {
                    switch(mode) {
                        case ApplicationMode.Xml: {
                                switch(node.NodeType) {
                                    case XmlNodeType.Element:
                                        var newElement = new XmlElementViewModel(node, commandTarget);
                                        return newElement;
                                    case XmlNodeType.Text:
                                        var newText = new XmlTextViewModel(node, commandTarget);
                                        return newText;
                                    default:
                                        return null;
                                }
                            }
                        case ApplicationMode.Xsd: {
                                switch(node.NodeType) {
                                    case XmlNodeType.Element:
                                        var newElement = new XsdElementViewModel(node, XsdElementParser.GetElementType(node.LocalName), commandTarget);
                                        return newElement;
                                    case XmlNodeType.Text:
                                        var newText = new XmlTextViewModel(node, commandTarget);
                                        return newText;
                                    default:
                                        return null;
                                }
                            }
                    }
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }

            return null;
        }

        /// <summary>
        /// Adds <paramref name="vertex"/> to the <seealso cref="Graph"/>.
        /// </summary>
        /// <param name="vertex">The vertex.</param>
        /// <param name="parentVertex">Parent of the param to add (if there is one).</param>
        private void AddToGraph(XmlNodeViewModel vertex, XmlNodeViewModel parentVertex) {
            try {
                // add new param to Graph
                this.Graph.AddVertex(vertex);

                // if this new param has a param param, connect them with edge
                if(parentVertex != null) {
                    var edge = new XmlNodeEdge(Guid.NewGuid().ToString(), parentVertex, vertex);
                    this.Graph.AddEdge(edge);
                }

                switch(vertex.Node.NodeType) {
                    case XmlNodeType.Element:
                        var element = vertex as XmlElementViewModel;

                        // clean up
                        if(element != null) {
                            foreach(var child in element.Children.Where(x => x != null)) {
                                this.RemoveNode(child.Node);
                            }

                            // and add its child nodes recursively
                            foreach(var child in element.Node.ChildNodes.Where(x => x != null)) {
                                XmlNodeViewModel v = CreateVertex(child, this.model.Mode, this.view as MainWindow);
                                if(v != null) {
                                    AddToGraph(v, vertex);
                                }
                            }
                        }

                        break;
                }

                // add this param to dictionary for easier lookup
                this.AddNode(vertex.Node, vertex);
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Insert a vertex after or before the reference vertex.
        /// </summary>
        /// <param name="vertexToInsert">Vertex to insert.</param>
        /// <param name="parentVertex">New parent vertex of vertex being inserted.</param>
        /// <param name="insertMode">Insertion mode.</param>
        /// <param name="referenceVertex">Reference vertex (left or right sibling according to the insert mode).</param>
        public void InsertAfter(XmlNodeViewModel vertexToInsert, XmlNodeViewModel parentVertex, InsertMode insertMode, XmlNodeViewModel referenceVertex) {
            try {
                var outEdges = this.Graph.OutEdges(parentVertex);
                var outVertices = from e in outEdges select e.Target;

                int referenceVertexIndex = outVertices.ToList().IndexOf(referenceVertex);
                var list = outVertices.ToList();
                switch(insertMode) {
                    case InsertMode.After:
                        list.Insert(referenceVertexIndex + 1, vertexToInsert);
                        break;
                    case InsertMode.Before:
                        list.Insert(referenceVertexIndex, vertexToInsert);
                        break;
                    case InsertMode.First:
                        list.Insert(0, vertexToInsert);
                        break;
                    case InsertMode.Last:
                        list.Add(vertexToInsert);
                        break;
                }

                foreach(var edge in outEdges.ToList()) {
                    Graph.RemoveEdge(edge);
                }

                this.AddNode(vertexToInsert.Node, vertexToInsert);
                this.Graph.AddVertex(vertexToInsert);
                foreach(var vertex in list) {
                    RenderNodeRecursively(vertex);

                    var edge = new XmlNodeEdge(Guid.NewGuid().ToString(), parentVertex, vertex);
                    this.Graph.AddVertex(vertex);
                    this.Graph.AddEdge(edge);
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Positions the dialog right down of the current mouse position.
        /// </summary>
        /// <param name="dialog">Dialog to position.</param>
        protected void PositionDialogRightDownFromMouse(Window dialog) {
            try {
                var mousePosition = this.view.GetCurrentMousePosition();
                dialog.Left = mousePosition.X;
                dialog.Top = mousePosition.Y;
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Gets the current selected <see cref="XmlElementViewModel"/>.
        /// </summary>
        /// <returns>Selected item.</returns>
        private XmlElementViewModel GetCurrentSelectedXmlElement() {
            var selectedItem = this.SelectionService.CurrentSelection.FirstOrDefault() as VertexControl;
            XmlElementViewModel selected = null;
            if(selectedItem != null) {
                selected = selectedItem.Vertex as XmlElementViewModel;
            }

            return selected;
        }

        /// <summary>
        /// Gets the current selected <see cref="XmlNodeViewModel"/>.
        /// </summary>
        /// <returns></returns>
        private XmlNodeViewModel GetCurrentSelectedXmlNode() {
            var selectedItem = this.SelectionService.CurrentSelection.FirstOrDefault() as VertexControl;
            XmlNodeViewModel selected = null;
            if(selectedItem != null) {
                selected = selectedItem.Vertex as XmlNodeViewModel;
            }

            return selected;
        }

        /// <summary>
        /// Gets the current selected <see cref="XmlTextViewModel"/>.
        /// </summary>
        /// <returns>Selected text node.</returns>
        private XmlTextViewModel GetCurrentSelectedTextNode() {
            var selectedItem = this.SelectionService.CurrentSelection.FirstOrDefault() as VertexControl;
            XmlTextViewModel selected = null;
            if(selectedItem != null) {
                selected = selectedItem.Vertex as XmlTextViewModel;
            }

            return selected;
        }
        #endregion

        #region Commands
        #region SelectNodes
        /// <summary>
        /// Determines whether the <see cref="SelectNodesCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanSelectNodes() {
            if(this.root != null) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Selects nodes according to the XPath query.
        /// </summary>
        /// <param name="xpath">XPath query.</param>
        private void SelectNodes(string xpath) {
            try {
                this.controller.SelectNodes(xpath);
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region Export
        /// <summary>
        /// Determines whether the <see cref="ExportCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanExport() {
            if(this.Graph != null) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Exports the current view as an image.
        /// </summary>
        private void Export() {
            try {
                this.view.ExportCanvas();
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region PrintCommand
        /// <summary>
        /// Determines whether the <see cref="ApplicationCommands.Print"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanPrint() {
            if(this.Graph != null) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Prints the current view.
        /// </summary>
        private void Print() {
            try {
                this.view.PrintCanvas();
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region ChangeColor
        /// <summary>
        /// Determines whether the <see cref="ChangeColorCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanChangeColor() {
            if(SelectionService.CurrentSelection.Count > 0) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Shows the dialog to change the colors of the current selection.
        /// </summary>
        private void ChangeColor() {
            try {
                var param = GetCurrentSelectedXmlNode();
                if(param == null) {
                    return;
                }

                var dialog = new ChangeColorDialog(param.VertexControl) { Owner = this.view as Window };
                var res = dialog.ShowDialog();
                if(res.HasValue && res.Value) {
                    var selectedItems = SelectionService.GetCurrentInstance(this.root.VertexControl).CurrentSelection.Cast<VertexControl>();
                    if(selectedItems.Count() > 0) {
                        this.model.Modified = true;
                    }

                    foreach(var item in selectedItems) {

                        item.FillBrush = dialog.FillBrush;
                        item.StrokeBrush = dialog.StrokeBrush;

                        if(item.Vertex is XmlNodeViewModel) {
                            var node = item.Vertex as XmlNodeViewModel;
                            VisualizationInfo appearance;
                            // saving the appearance for future reference (so the changed appearance stays changed)
                            if(this.appearances.TryGetValue(node.Node, out appearance)) {
                                appearance.Details.Background = item.FillBrush;
                                appearance.Details.Stroke = item.StrokeBrush;
                            } else {
                                var app = new VisualizationInfo(node.Node.Address, item.FillBrush, item.StrokeBrush, item.AppearanceTemplateName);
                                this.appearances[node.Node] = app;
                                node.Node.Appearance = app.Details;
                            }
                        }
                    }
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region ChangeAppearance
        /// <summary>
        /// Determines whether the <see cref="ChangeAppearanceCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanChangeAppearance() {
            if(SelectionService.CurrentSelection.Count > 0) {
                return true;
            } else {
                return false;
            }
        }

        private object FindResource(string resourceName) {
            var template = Application.Current.TryFindResource(resourceName);
            return template;
        }

        /// <summary>
        /// Changes the appearance of the current selection.
        /// </summary>
        private void ChangeAppearance() {
            try {
                var dialog = new ChangeAppearanceDialog { Owner = this.view as Window };
                var res = dialog.ShowDialog();
                if(res.HasValue && res.Value) {
                    var selectedAppearance = dialog.SelectedAppearance;
                    var template = FindResource(selectedAppearance) as ControlTemplate;
                    var selectedItems = SelectionService.GetCurrentInstance(this.root.VertexControl).CurrentSelection.Cast<VertexControl>();
                    if(selectedItems.Count() > 0 && template != null) {
                        this.model.Modified = true;
                        foreach(var item in selectedItems) {
                            item.AppearanceTemplate = template;
                            item.AppearanceTemplateName = selectedAppearance;

                            if(item.Vertex is XmlNodeViewModel) {
                                var node = item.Vertex as XmlNodeViewModel;
                                VisualizationInfo appearance;
                                // if the changed appearance if saved in appearances collection, leave it
                                if(this.appearances.TryGetValue(node.Node, out appearance)) {
                                    appearance.Details.AppearanceTemplate = selectedAppearance;
                                } else {
                                    // if the appearance has not been saved to the collection, add it there for future reference
                                    var app = new VisualizationInfo(node.Node.Address, item.FillBrush, item.StrokeBrush, item.AppearanceTemplateName);
                                    this.appearances[node.Node] = app;
                                    node.Node.Appearance = app.Details;
                                }
                            }
                        }
                    }
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region MoveNode
        /// <summary>
        /// Determines whether the MoveNode commands can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanMoveNode(XmlNodeViewModel node) {
            try {
                if(node.Node.Parent == null) {
                    return false;
                } else {
                    if(node.Node.Parent.TotalChildCount > 1) {
                        return true;
                    }
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }

            return false;
        }

        /// <summary>
        /// Moves the node to the left among node's siblings.
        /// </summary>
        /// <param name="node">Node to move.</param>
        private void MoveNodeLeft(XmlNodeViewModel node) {
            try {
                var xmlNode = node.Node;
                var parentNode = xmlNode.Parent;
                BigXmlNode previousSibling = null;
                if(parentNode != null) {
                    previousSibling = parentNode.GetPreviousSibling(xmlNode);
                }

                if(previousSibling != null) {
                    previousSibling = parentNode.GetPreviousSibling(previousSibling);
                }

                this.controller.ChangeNodeOrder(node.Node, previousSibling);
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Moves the node to the right among node's siblings.
        /// </summary>
        /// <param name="node">Node to move.</param>
        private void MoveNodeRight(XmlNodeViewModel node) {
            try {
                var xmlNode = node.Node;
                var parentNode = xmlNode.Parent;
                BigXmlNode previousSibling = null;
                if(parentNode != null) {
                    previousSibling = parentNode.GetNextSibling(xmlNode);
                }

                this.controller.ChangeNodeOrder(node.Node, previousSibling);
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region Copy & move commands
        /// <summary>
        /// Determines whether the <see cref="SelectSubtreeForCopyingCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanSelectSubtreeForCopying(XmlNodeViewModel param) {
            if(param == null) {
                param = GetCurrentSelectedXmlNode();
                if(param == null) {
                    return false;
                }
            }

            if(param != null) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Selects subtree for copy operation.
        /// </summary>
        /// <param name="param">Root of the subtree.</param>
        private void SelectSubtreeForCopying(XmlNodeViewModel param) {
            try {
                if(param == null) {
                    param = GetCurrentSelectedXmlNode();
                    if(param == null) {
                        return;
                    }
                }

                this.subtreeForCopyingOrMoving = param;
                this.subtreeOperation = SubtreeOperation.Copy;
                this.SubtreeSelected = true;
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Determines whether the <see cref="SelectSubtreeForMovingCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanSelectSubtreeForMoving(XmlNodeViewModel param) {
            if(param == null) {
                param = GetCurrentSelectedXmlNode();
                if(param == null) {
                    return false;
                }
            }

            if(param != null) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Selects subtree for move operation.
        /// </summary>
        /// <param name="param">Root of the subtree.</param>
        private void SelectSubtreeForMoving(XmlNodeViewModel param) {
            try {
                if(param == null) {
                    param = GetCurrentSelectedXmlNode();
                    if(param == null) {
                        return;
                    }
                }

                this.subtreeForCopyingOrMoving = param;
                this.subtreeOperation = SubtreeOperation.Move;
                this.SubtreeSelected = true;
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Determines whether the <see cref="PasteSubtreeCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanPasteSubtree() {
            return this.SubtreeSelected;
        }

        /// <summary>
        /// Pastes the selected subtree at the specified place.
        /// </summary>
        /// <param name="param">Parent where to paste the selected subtree.</param>
        private void PasteSubtree(XmlNodeViewModel param) {
            try {
                if(param == null) {
                    param = GetCurrentSelectedXmlNode();
                    if(param == null) {
                        return;
                    }
                }

                switch(this.subtreeOperation) {
                    case SubtreeOperation.Copy:
                        this.controller.CopySubtree(this.subtreeForCopyingOrMoving.Node, param.Node, InsertMode.Last, null);
                        this.SubtreeSelected = false;
                        this.subtreeOperation = SubtreeOperation.None;
                        break;
                    case SubtreeOperation.Move:
                        this.controller.MoveSubtree(this.subtreeForCopyingOrMoving.Node, param.Node, InsertMode.Last, null);
                        this.SubtreeSelected = false;
                        this.subtreeOperation = SubtreeOperation.None;
                        break;
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region ValidateXsdCommand
        /// <summary>
        /// Determines whether the XSD validation can be perfomed. It can be done when
        /// there is a root element and validation is not being performed.
        /// </summary>
        /// <returns>True if validation can be done, false otherwise.</returns>
        private bool CanValidateXsd() {
            if(this.root == null) {
                return false;
            }

            return !this.ValidatingXsd;
        }

        /// <summary>
        /// Validates the document against XSD (async).
        /// </summary>
        private void ValidateXsd() {
            try {
                this.controller.ValidateXsdAsync();
                this.ValidatingXsd = true;
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Cancels the XSD validation.
        /// </summary>
        private void CancelValidateXsd() {
            try {
                this.controller.CancelValidateXsd();
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region ValidateDtdCommand
        /// <summary>
        /// Determines whether the DTD validation can be perfomed. It can be done when
        /// there is a root element and validation is not being performed.
        /// </summary>
        /// <returns>True if validation can be done, false otherwise.</returns>
        private bool CanValidateDtd() {
            if(this.root == null) {
                return false;
            }

            return !this.ValidatingDtd;
        }

        /// <summary>
        /// Starts DTD validation.
        /// </summary>
        private void ValidateDtd() {
            try {
                this.controller.ValidateDtdAsync();
                this.ValidatingDtd = true;
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Cancels DTD validation.
        /// </summary>
        private void CancelValidateDtd() {
            try {
                this.controller.CancelValidateDtd();
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Determines whether the <see cref="AttachDtdCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanAttachDtd() {
            if(this.model.Mode != ApplicationMode.Xml) {
                return false;
            }

            if(this.root == null) {
                return false;
            } else {
                return true;
            }
        }

        /// <summary>
        /// Attaches the DTD to the document.
        /// </summary>
        private void AttachDtd() {
            try {
                var dlg = new Microsoft.Win32.OpenFileDialog { DefaultExt = ".dtd", Filter = "DTD Documents|*.dtd" };

                var result = dlg.ShowDialog();
                if(result.HasValue && result.Value) {
                    var path = dlg.FileName;
                    var fi = new FileInfo(path);
                    if(fi.DirectoryName == Directory.GetCurrentDirectory()) {
                        path = fi.Name;
                    }

                    this.controller.SetDocumentType(this.root.Node.Name, null, path, null);
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Determines whether the <see cref="RemoveDtdCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanRemoveDtd() {
            if(this.model.Mode != ApplicationMode.Xml) {
                return false;
            }

            return this.model.DocumentType != null;
        }

        /// <summary>
        /// Removes the DTD from the document.
        /// </summary>
        private void RemoveDtd() {
            try {
                this.controller.RemoveDocumentType();
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region NewCommand
        /// <summary>
        /// Determines whether the <see cref="ApplicationCommands.New"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanNewDocument() {
            return true;
        }

        /// <summary>
        /// Creates new empty document.
        /// </summary>
        /// <param name="type">Type of the document to create.</param>
        private void NewDocument(string type) {
            try {
                switch(type) {
                    case "xml": {
                            var window = new MainWindow(this.application, ApplicationMode.Xml);
                            window.Show();
                            //this.model.Mode = ApplicationMode.Xml;
                            break;
                        }
                    case "xsd": {
                            // TODO: add implementation
                            var window = new MainWindow(this.application, ApplicationMode.Xsd);
                            window.Show();
                            //this.model.Mode = ApplicationMode.Xsd;
                            break;
                        }
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region RemoveNodeCommand
        /// <summary>
        /// Determines whether the <see cref="RemoveNodeCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanRemoveNode() {
            if(SelectionService.CurrentSelection.Count == 1) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Pops up the dialog to remove the param.
        /// </summary>
        /// <param name="param">Reference to param which is about to be deleted.</param>
        private void RemoveNode(XmlNodeViewModel param) {
            try {
                if(param == null) {
                    param = GetCurrentSelectedXmlNode();
                    if(param == null) {
                        return;
                    }
                }

                if(param.Node.TotalChildCount > 0) {
                    var dialog = new RemoveNodeDialog() { Owner = this.view as Window };
                    var res = dialog.ShowDialog();

                    if(!dialog.Canceled && res.HasValue && res.Value) {
                        // remove with subtree
                        this.controller.RemoveNode(param.Node, true);
                    } else if(!dialog.Canceled && res.HasValue && !res.Value) {
                        // don't remove subtree
                        this.controller.RemoveNode(param.Node, false);
                    }
                } else {
                    this.controller.RemoveNode(param.Node, true);
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            } finally {
                SelectionService.ClearSelection();
            }
        }
        #endregion

        #region ExpandAllCommand
        /// <summary>
        /// Determines whether the <see cref="ExpandAllCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanExpandAll() {
            if(this.root != null && this.root.Node.OriginalAddress == null) {
                return false;
            }

            return this.root != null;
        }

        /// <summary>
        /// Expands all nodes in the document.
        /// </summary>
        private void ExpandAll() {
            try {
                if(this.model.TotalNodeCount > nodeLimit) {
                    var res = MessageBox.Show(LanguageDictionary.GetDictionary(LanguageContext.Instance.Culture).Translate<string>("viewModel.TooManyNodes", "Text"),
                    LanguageDictionary.GetDictionary(LanguageContext.Instance.Culture).Translate<string>("viewModel.Warning", "Text"),
                    MessageBoxButton.YesNo,
                    MessageBoxImage.Warning);

                    if(res == MessageBoxResult.Yes) {
                        this.controller.ExpandAll();
                    }
                } else {
                    this.controller.ExpandAll();
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region CreateRootElementCommand
        /// <summary>
        /// Determines whether the <see cref="CreateRootElementCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanCreateRootElement() {
            if(this.model.Mode != ApplicationMode.Xml) {
                return false;
            }

            return this.root == null;
        }

        /// <summary>
        /// Creates root element of the document.
        /// </summary>
        private void CreateRootElement() {
            try {
                var element = this.controller.CreateElement("newElement");

                var newElement = CreateVertex(element, this.model.Mode, this.view as MainWindow) as XmlElementViewModel; //new XmlElementViewModel(element);

                if(newElement == null) {
                    throw new InvalidOperationException("Created view model is invalid!");
                }

                var editXmlElementDialog = new EditXmlElementDialog(newElement);
                PositionDialogRightDownFromMouse(editXmlElementDialog);
                var res = editXmlElementDialog.ShowDialog();

                if(res.HasValue && res.Value) {
                    this.controller.CreateRootElement(newElement.Prefix, newElement.LocalName, newElement.NamespaceURI, newElement.Attributes);
                }
            } catch(Exception ex) {
                var rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region AddChildNodeCommand
        /// <summary>
        /// Determines whether the <see cref="AddChildElementCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanAddChildElement(XmlElementViewModel parent) {
            if(this.model.Mode != ApplicationMode.Xml) {
                return false;
            }

            if(parent == null) {
                parent = GetCurrentSelectedXmlElement();
            }

            if(parent != null) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Adds the child element.
        /// </summary>
        /// <param name="parent">Parent where the child element is being added.</param>
        private void AddChildElement(XmlElementViewModel parent) {
            try {
                if(parent == null) {
                    parent = GetCurrentSelectedXmlElement();
                    if(parent == null) {
                        return;
                    }
                }

                var element = this.controller.CreateElement("element");

                var newElement = CreateVertex(element, this.model.Mode, this.view as MainWindow) as XmlElementViewModel; //new XmlElementViewModel(element);

                if(newElement == null) {
                    throw new InvalidOperationException("Created view model is invalid!");
                }

                var editXmlElementDialog = new EditXmlElementDialog(newElement) { Owner = this.view as Window };
                //PositionDialogRightDownFromMouse(editXmlElementDialog);
                bool? res = editXmlElementDialog.ShowDialog();

                if(res.HasValue && res.Value) {
                    var attributes = newElement.Attributes.ToDictionary(att => att.Name);

                    this.controller.AddChildElement(parent.Node, newElement.Prefix, newElement.NamespaceURI, newElement.LocalName, attributes, InsertMode.Last, null);
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Determines whether the <see cref="AddChildTextNodeCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanAddChildTextNode(XmlElementViewModel parent) {
            if(this.model.Mode != ApplicationMode.Xml) {
                return false;
            }

            if(parent == null) {
                parent = GetCurrentSelectedXmlElement();
            }

            if(parent != null) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Adds child text node to the parent.
        /// </summary>
        /// <param name="parent">Parent node where to add the text node.</param>
        private void AddChildTextNode(XmlElementViewModel parent) {
            try {
                if(parent == null) {
                    parent = GetCurrentSelectedXmlElement();
                    if(parent == null) {
                        return;
                    }
                }

                var textNode = this.controller.CreateTextNode("text");
                var newTextNode = CreateVertex(textNode, this.model.Mode, this.view as MainWindow) as XmlTextViewModel; //new XmlTextViewModel(textNode);

                if(newTextNode == null) {
                    throw new InvalidOperationException("Created view model is invalid!");
                }

                var editTextNodeDialog = new EditTextNodeDialog(newTextNode) { Owner = this.view as Window };
                //PositionDialogRightDownFromMouse(editTextNodeDialog);
                var res = editTextNodeDialog.ShowDialog();

                if(res.HasValue && res.Value) {
                    this.controller.AddChildTextNode(parent.Node, newTextNode.Text, InsertMode.Last, null);
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region SettingsCommand
        /// <summary>
        /// Shows the settings dialog.
        /// </summary>
        private void Settings() {
            try {
                var dialog = new SettingsDialog { Owner = this.view as Window };
                bool? res = dialog.ShowDialog();

                if(res.HasValue && res.Value) {
                    XmlStudioSettings.Instance.Save();
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region EditTextNodeCommand
        /// <summary>
        /// Determines whether the <see cref="EditTextNodeCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanEditTextNode() {
            if(this.model.Mode != ApplicationMode.Xml) {
                return false;
            }

            if(SelectionService.CurrentSelection.Count == 1) {
                var node = GetCurrentSelectedTextNode();
                if(node != null) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        /// <summary>
        /// Shows the dialog to edit text node.
        /// </summary>
        /// <param name="textNode">Text node to edit.</param>
        private void EditTextNode(XmlTextViewModel textNode) {
            try {
                var dialog = new EditTextNodeDialog(textNode) { Owner = this.view as Window };
                //PositionDialogRightDownFromMouse(dialog);
                bool? result = dialog.ShowDialog();

                if(result.HasValue && result.Value) {
                    try {
                        this.controller.ModifyTextNode(textNode.Node, textNode.Text);
                    } catch(Exception ex) {
                        bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                        if(rethrow) {
                            throw;
                        }
                    }
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region SaveDocumentCommand
        /// <summary>
        /// Determines whether the <see cref="SaveDocumentCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanSaveDocument() {
            return this.model.Modified;
        }

        /// <summary>
        /// Saves the document to the file.
        /// </summary>
        private void SaveDocument() {
            try {
                this.controller.Save();
                this.SaveVisualization();
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region SaveDocumentAsCommand
        /// <summary>
        /// Determines whether the <see cref="SaveDocumentAsCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanSaveDocumentAs() {
            return this.Graph != null;
        }

        /// <summary>
        /// Saves the document to the file that the user specifies in SaveFileDialog.
        /// </summary>
        private void SaveDocumentAs() {
            try {
                var dlg = new Microsoft.Win32.SaveFileDialog {
                    DefaultExt = this.model.Mode == ApplicationMode.Xml ? "*.xml" : "*.xsd",
                    Filter = this.model.Mode == ApplicationMode.Xml ? "Xml Documents|*.xml|Xsd Documents|*.xsd" : "Xsd Documents|*.xsd|Xml Documents|*.xml"
                };

                var result = dlg.ShowDialog();
                if(result == true) {
                    try {
                        string path = dlg.FileName;
                        this.controller.Save(path);
                        this.SaveVisualization();
                    } catch(Exception ex) {
                        bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                        if(rethrow) {
                            throw;
                        }
                    }
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region UndoCommand
        /// <summary>
        /// Undoes the last action.
        /// </summary>
        private void Undo() {
            try {
                this.controller.Undo();
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region RedoCommand
        /// <summary>
        /// Redoes the last action.
        /// </summary>
        private void Redo() {
            try {
                this.controller.Redo();
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region EditXmlNodeCommand
        /// <summary>
        /// Determines whether the <see cref="EditXmlNodeCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanEditXmlNode() {
            if(this.model.Mode != ApplicationMode.Xml) {
                return false;
            }

            var selectedItem = GetCurrentSelectedXmlNode();
            if(selectedItem == null) {
                return false;
            }

            if(this.SelectionService.CurrentSelection.Count == 1) {
                return true;
            } else if(this.SelectionService.CurrentSelection.Count > 1) {
                return false;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Edits either element or text node.
        /// </summary>
        private void EditXmlNode() {
            var param = GetCurrentSelectedXmlNode();
            if(param == null) {
                return;
            }

            if(param is XmlElementViewModel) {
                EditXmlElement(param as XmlElementViewModel);
            } else if(param is XmlTextViewModel) {
                EditTextNode(param as XmlTextViewModel);
            }
        }
        #endregion

        #region EditXmlElementCommand
        /// <summary>
        /// Determines whether the <see cref="EditXmlElementCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanEditXmlElement() {
            if(this.model.Mode != ApplicationMode.Xml) {
                return false;
            }

            var selectedItem = GetCurrentSelectedXmlElement();
            if(selectedItem == null) {
                return false;
            }

            if(this.SelectionService.CurrentSelection.Count == 1) {
                return true;
            } else if(this.SelectionService.CurrentSelection.Count > 1) {
                return false;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Shows the dialog to edit selected element.
        /// </summary>
        /// <param name="param">Element to edit.</param>
        private void EditXmlElement(XmlElementViewModel param) {
            try {
                if(param == null) {
                    param = GetCurrentSelectedXmlElement();
                    if(param == null) {
                        return;
                    }
                }

                var editXmlElementDialog = new EditXmlElementDialog(param) { Owner = this.view as Window };
                //PositionDialogRightDownFromMouse(editXmlElementDialog);
                var res = editXmlElementDialog.ShowDialog();

                if(res.HasValue && res.Value) {
                    this.SaveXmlElement(param);
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region SaveXmlElementCommand
        /// <summary>
        /// Determines whether the <see cref="SaveXmlElementCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanSaveXmlElement(XmlElementViewModel element) {
            return true;
        }

        /// <summary>
        /// Saves the updated element to the underlying document.
        /// </summary>
        /// <param name="element"><see cref="XmlElementViewModel"/> to save.</param>
        protected void SaveXmlElement(XmlElementViewModel element) {
            try {
                // necessary because simpleTypes get update after rename from the original param so we lose the changes
                var attributes = element.Attributes.ToDictionary(att => att.Name);

                this.controller.RenameElement(element.Node, element.LocalName, element.NamespaceURI, element.Prefix);
                this.controller.UpdateAttributes(element.Node, attributes);

                element.BindElement();
                element.BindAttributes();
                element.IsModified = false;
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region SaveVisualizationCommand
        private bool CanSaveVisualization() {
            if(this.Graph != null) {
                return true;
            } else {
                return false;
            }
        }

        private const string XmlStudioVisualizationExtension = ".xmlStudio";

        /// <summary>
        /// Helper class for saving the visualization.
        /// </summary>
        [Serializable]
        [XmlInclude(typeof(Brush))]
        [XmlInclude(typeof(SolidColorBrush))]
        [XmlInclude(typeof(MatrixTransform))]
        public class VisualizationInfoDetails : IAppearance {
            /// <summary>
            /// Gets or sets the background brush.
            /// </summary>
            public Brush Background { get; set; }

            /// <summary>
            /// Gets or sets the stroke brush.
            /// </summary>
            public Brush Stroke { get; set; }

            /// <summary>
            /// Gets or sets the name of the appearance template.
            /// </summary>
            public string AppearanceTemplate { get; set; }

            /// <summary>
            /// Overriden so two items are compared by their content.
            /// </summary>
            /// <param name="obj">Another instance to compare.</param>
            /// <returns>True if both instances have the same content, false otherwise.</returns>
            public override bool Equals(object obj) {
                if(!(obj is VisualizationInfoDetails))
                    return false;

                if(obj == null)
                    return false;

                VisualizationInfoDetails t = (VisualizationInfoDetails)obj;

                return Background.Equals(t.Background) && Stroke.Equals(t.Stroke) && AppearanceTemplate.Equals(t.AppearanceTemplate);
            }

            /// <summary>
            /// Returns the hashcode for the value of this instance.
            /// </summary>
            /// <returns>Hashcode.</returns>
            public override int GetHashCode() {
                return Background.GetHashCode() ^ Stroke.GetHashCode() ^ AppearanceTemplate.GetHashCode();
            }
        }

        /// <summary>
        /// Helper class for serialization of visualization.
        /// </summary>
        [Serializable]
        [XmlInclude(typeof(Brush))]
        [XmlInclude(typeof(SolidColorBrush))]
        [XmlInclude(typeof(MatrixTransform))]
        [XmlInclude(typeof(VisualizationInfoDetails))]
        public class VisualizationInfo {
            /// <summary>
            /// Gets or sets the address.
            /// </summary>
            public int[] Address { get; set; }

            private VisualizationInfoDetails details = new VisualizationInfoDetails();

            /// <summary>
            /// Gets or sets the details.
            /// </summary>
            /// <value>The details.</value>
            public VisualizationInfoDetails Details {
                get { return this.details; }
                set { this.details = value; }
            }

            /// <summary>
            /// Initializes a new instance of the <see cref="VisualizationInfo"/> class.
            /// </summary>
            public VisualizationInfo() { }

            /// <summary>
            /// Initializes a new instance of the <see cref="VisualizationInfo"/> class.
            /// </summary>
            /// <param name="address"></param>
            public VisualizationInfo(int[] address) {
                this.Address = address;
            }

            /// <summary>
            /// Initializes a new instance of the <see cref="VisualizationInfo"/> class.
            /// </summary>
            /// <param name="address">Node's address.</param>
            /// <param name="background">Node's background brush.</param>
            /// <param name="stroke">Node's stroke brush.</param>
            /// <param name="appearanceTemplate">Name of the appearance template.</param>
            public VisualizationInfo(int[] address, Brush background, Brush stroke, string appearanceTemplate) {
                this.Address = address;
                this.Details.Background = background;
                this.Details.Stroke = stroke;
                this.Details.AppearanceTemplate = appearanceTemplate;
            }

            /// <summary>
            /// Overriden so two items are compared by their content.
            /// </summary>
            /// <param name="obj">Another instance to compare.</param>
            /// <returns>True if both instances have the same content, false otherwise.</returns>
            public override bool Equals(object obj) {
                if(!(obj is VisualizationInfo))
                    return false;

                if(obj == null)
                    return false;

                VisualizationInfo t = (VisualizationInfo)obj;

                return Address.SequenceEqual(t.Address) && Details.Equals(t.Details);
            }

            /// <summary>
            /// Returns the hashcode for the value of this instance.
            /// </summary>
            /// <returns>Hashcode.</returns>
            public override int GetHashCode() {
                return Address.GetHashCode() ^ Details.GetHashCode();
            }
        }

        private void SaveVisualization() {
            var file = new XElement("File", this.model.DocumentName);
            var elements = new XElement("Elements");

            XmlSerializer xs = new XmlSerializer(typeof(VisualizationInfo));

            foreach(var node in this.nodes.Values) {
                var info = new VisualizationInfo(node.Node.Address, node.VertexControl.Background, node.VertexControl.StrokeBrush, node.VertexControl.AppearanceTemplateName);

                var ms = new MemoryStream();
                xs.Serialize(ms, info);
                ms.Seek(0, SeekOrigin.Begin);
                var sr = new StreamReader(ms);
                var infoSerialized = sr.ReadToEnd();
                ms.Close();

                elements.Add(new XElement("Element",
                    new XElement("Info", new XCData(infoSerialized))
                    ));
            }

            var visualization = new XDocument(new XElement("XmlStudio", new XElement("Visualization", file, elements)));
            visualization.Save(this.model.DocumentName + XmlStudioVisualizationExtension);
        }

        //private List<VisualizationInfo> LoadedVisualizationInfo = new List<VisualizationInfo>();
        private HashSet<VisualizationInfo> LoadedVisualizationInfo = new HashSet<VisualizationInfo>();

        private void LoadVisualization() {
            LoadedVisualizationInfo.Clear();
            var file = new FileInfo(model.DocumentName + XmlStudioVisualizationExtension);
            if(file.Exists) {
                var elements = from x in XElement.Load(file.Name).Elements("Visualization").Elements("Elements") select x;
                foreach(var element in elements.Elements()) {
                    var info = element.Element("Info");
                    var cdata = info.Nodes().OfType<XCData>().FirstOrDefault();
                    if(cdata != null) {
                        var serializedInfo = cdata.Value;
                        var deserializedInfo = new XmlSerializer(typeof(VisualizationInfo)).Deserialize(new MemoryStream(Encoding.Default.GetBytes(serializedInfo))) as VisualizationInfo;
                        LoadedVisualizationInfo.Add(deserializedInfo);
                    }
                }
            }
        }
        #endregion

        #region CollapseNodeCommand
        /// <summary>
        /// Determines whether the <see cref="CollapseNodeCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanCollapseNode(XmlElementViewModel param) {
            if(param.Node.NodeState == ExpandState.Collapsed) {
                return false;
            } else {
                return true;
            }
        }

        /// <summary>
        /// Collapses the specified node.
        /// </summary>
        /// <param name="param">Node to collapse.</param>
        private void CollapseNode(XmlElementViewModel param) {
            try {
                this.controller.CollapseNode(param.Node);
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region RelayoutCommand
        private bool CanRelayout() {
            return true;
        }

        /// <summary>
        /// Relayouts this instance.
        /// </summary>
        protected static void Relayout() {
            try {
                LayoutManager.Instance.Relayout();
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region ExitCommand
        /// <summary>
        /// Determines whether the <see cref="ExitCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanExit() {
            return true;
        }

        /// <summary>
        /// Executes the <see cref="ExitCommand"/>.
        /// </summary>
        private void Exit() {
            try {
                this.application.CloseAllWindows();
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region CloseCommand
        /// <returns>
        /// True if command can be executed, false otherwise.
        /// </returns>
        private bool CanClose() {
            return true;
        }

        /// <summary>
        /// Calls cleanup methods.
        /// </summary>
        private void Close() {
            try {
                this.view.Close();
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region ExpandNodeCommand
        /// <summary>
        /// Determines whether the <see cref="ExpandNodeCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanExpandNode(XmlElementViewModel param) {
            if(param.Node.NodeState == ExpandState.Expanded) {
                return false;
            } else {
                return true;
            }
        }

        /// <summary>
        /// Expands the specified node.
        /// </summary>
        /// <param name="parent">Node to expand.</param>
        private void ExpandNode(XmlElementViewModel parent) {
            try {
                int childCount = parent.Node.TotalChildCount;
                if(childCount > XmlStudioSettings.Instance.NodeCountToExpand) {
                    var dialog = new RangeDialog(childCount) { Owner = this.view as Window };
                    //PositionDialogRightDownFromMouse(dialog);
                    bool? res = dialog.ShowDialog();
                    if(res.HasValue && res.Value) {
                        string range = dialog.Range;
                        this.controller.ExpandNode(parent.Node, range);
                    }
                } else {
                    this.controller.ExpandNode(parent.Node, string.Empty);
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }

        /// <summary>
        /// Determines whether the <see cref="ExpandNodeWithSubtreeCommand"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanExpandWithSubtree(XmlElementViewModel param) {
            try {
                if(param == null) {
                    param = GetCurrentSelectedXmlElement();
                    if(param == null) {
                        return false;
                    }
                }

                if(param.Node.TotalChildCount == 0) {
                    return false;
                }

                if(param.Node.NodeState != ExpandState.Expanded) {
                    return true;
                } else {
                    return false;
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }

            return false;
        }

        /// <summary>
        /// Expands the node and all its descendants.
        /// </summary>
        /// <param name="param">Node to expand.</param>
        private void ExpandNodeWithSubtree(XmlElementViewModel param) {
            try {
                if(param == null) {
                    param = GetCurrentSelectedXmlElement();
                    if(param == null) {
                        return;
                    }
                }

                var nodes = this.model.CountNodes(param.Node.Address);
                if(nodes > nodeLimit) {
                    var res = MessageBox.Show(LanguageDictionary.GetDictionary(LanguageContext.Instance.Culture).Translate<string>("viewModel.TooManyNodes", "Text"),
                    LanguageDictionary.GetDictionary(LanguageContext.Instance.Culture).Translate<string>("viewModel.Warning", "Text"),
                    MessageBoxButton.YesNo,
                    MessageBoxImage.Warning);
                    if(res == MessageBoxResult.Yes) {
                        this.controller.ExpandNode(param.Node, string.Empty, true);
                    }
                } else {
                    this.controller.ExpandNode(param.Node, string.Empty, true);
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion

        #region OpenFileCommand
        /// <summary>
        /// Determines whether the <see cref="ApplicationCommand.Open"/> can be executed.
        /// </summary>
        /// <returns>True if command can be executed, false otherwise.</returns>
        private bool CanOpen() {
            return true;
        }

        /// <summary>
        /// Shows the dialog window to choose the file to open. Then opens it.
        /// </summary>
        private void OpenFile() {
            var dlg = new Microsoft.Win32.OpenFileDialog { DefaultExt = ".xml", Filter = "Xml Documents|*.xml|Xsd Documents|*.xsd|Xml Files|*.xml;*.xsd|All Files|*.*" };

            var result = dlg.ShowDialog();

            if(result == true) {
                try {
                    string path = dlg.FileName;
                    var extension = new FileInfo(path).Extension;
                    ApplicationMode modeToSet = ApplicationMode.Xml;
                    if(extension.ToLower() == ".xml") {
                        modeToSet = ApplicationMode.Xml;
                    } else if(extension.ToLower() == ".xsd") {
                        modeToSet = ApplicationMode.Xsd;
                    }

                    if(!string.IsNullOrEmpty(this.model.Path) || this.model.Modified || modeToSet != this.model.Mode) {
                        if(extension == ".xml") {
                            var window = new MainWindow(this.application, path, ApplicationMode.Xml);
                            window.Show();
                        } else if(extension == ".xsd") {
                            var window = new MainWindow(this.application, path, ApplicationMode.Xsd);
                            window.Show();
                        }
                    } else {
                        OpenFile(path);
                    }
                } catch(Exception ex) {
                    bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                    if(rethrow) {
                        throw;
                    }
                }
            }
        }

        private void OpenFile(string path) {
            try {
                var extension = new FileInfo(path).Extension;
                bool res = false;
                if(extension == ".xml") {
                    res = this.controller.OpenXmlFile(path);
                    //this.controller.GetRootElement();
                } else if(extension == ".xsd") {
                    res = this.controller.OpenXsdFile(path);
                    //this.controller.GetRootElement();
                }

                // Load visualization only if the mode hasn't been changed
                if(res == false) {
                    this.LoadVisualization();
                }
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            }
        }
        #endregion
        #endregion
        #endregion

        #region Events
        /// <summary>
        /// Occurs when one of the properties change.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
    }
}